export type ProgressSetting = {
    timing?: {
        delay?: number,
        interval?: number,
        auto?: boolean
    },
    target?: HTMLElement,
    color?: {
        start?: string,
        end?: string,
    },
    width?: {
        initial?: string
        radius?: boolean | string
        autoRun?: number
        end?: string
    },
    height?: string
}
export function deepCopy<T>(data: T) {
    return JSON.parse(JSON.stringify(data))
}

export function RAF(callback: Function, timeout?: number, timer?: { timeout?: number }) {
    return new Promise<typeof RAF>((res, rej) => {
        try {
            if (timeout) {

                let timerTimeout = setTimeout(() => {
                    requestAnimationFrame(() => {
                        callback()
                        res(RAF)
                    })
                }, timeout)
                if (timer) {
                    timer.timeout = timerTimeout
                }
            } else {
                requestAnimationFrame(() => {
                    callback()
                    res(RAF)
                })
            }
        } catch {
            rej()
        }
    })
}
/**
 * 默认设置
 */
const defaultSetting: ProgressSetting = {
    timing: {
        delay: 500,
        interval: 500,
        auto: true,
    },
    color: {
        start: 'rgb(0, 255, 195)',
        end: '#67c23a',
    },
    width: {
        initial: '20%',
        radius: true,
        autoRun: 0.1,
        end: "20%"
    },
    height: '3px'
}
// 数据覆盖工具
/**
 * 旧数据对于新数中的数据 在新数据中有的 拿过来 没有的保留
 * @param o 原本数据
 * @param n 新数据
 * @returns 返回覆盖后的数据
 */
export function coverData<T extends { [key: string]: any | undefined }>(o: T, n: T): T {
    // 新数据
    let r1 = Object.entries<T | undefined>(n)
    r1.forEach((data) => {
        if (typeof data[1] == 'undefined') {
            return
        }
        if (Array.isArray(data[1]) || typeof data[1] != 'object') {
            Object.defineProperty(o, data[0], {
                value: data[1]
            })
            return
        }
        return coverData(o[data[0]] as T, data[1])
    })
    return o
}
export class Progress {
    #target: HTMLElement = document.body
    #settingVal: ProgressSetting = deepCopy(defaultSetting)
    #back: HTMLElement | null = null
    #bar: HTMLElement = document.createElement('div')
    // dom display none
    #interval: number = 0
    #onStart: boolean = false
    #progress: number = 0
    /**
     * 开始进度条
     * @returns 
     */
    start() {
        if (!this.#back) {
            return
        }
        clearInterval(this.#interval)
        // 初始化
        this.#back.style.display = 'none'
        this.#bar.style.opacity = '1'
        this.#bar.style.width = '0px'
        this.#onStart = true
        this.#progress = 0
        // 下一帧开始展示组件
        RAF(() => {
            if (!this.#back) {
                return
            }
            this.#back.style.display = 'block'
        }).then((raf) => {
            // 继续下一帧
            raf(() => {
                // 如果现在还出去进入状态就初始化进度条长度
                if (this.#onStart) {
                    this.#bar.style.width = this.#settingVal.width?.initial!
                    // 自动动画 模拟进度
                    if (this.#settingVal.timing?.auto) {
                        this.#auto()
                    }
                }
            })
        })
    }
    // 自动动画
    #auto() {
        this.#interval = setInterval(() => {
            this.#progress = this.#progress + this.#settingVal.width?.autoRun!
            if (this.#progress >= 1) {
                this.#progress = 1
            }
            this.to()
        }, this.#settingVal.timing?.interval)
    }
    /**
     * 结束进度条
     * @returns 
     */
    end() {
        if (!this.#back) {
            return
        }
        clearInterval(this.#interval)
        this.#onStart = false
        RAF(() => {
            this.#bar.style.width = '100%'
            this.#bar.style.opacity = '0'
        }, this.#settingVal.timing?.delay).then(() => {
            // dom不显示
            let fun = () => {
                if (!this.#back) {
                    return
                }
                if (this.#back && !this.#onStart) {
                    this.#back.style.display = 'none'
                }
                this.#bar.removeEventListener('transitionend', fun)
            }
            this.#bar.addEventListener('transitionend', fun)

        })
    }
    /**
     * 让进度条去到百分之多少
     * @param percent 百分比 小数
     */
    to(percent?: number) {
        if (!this.#back && !this.#onStart) {
            return
        }
        if (!percent) {
            percent = this.#progress
        }
        this.#bar.style.width = `calc( ${this.#settingVal.width?.initial} + ( 100% - ${this.#settingVal.width?.initial} - ${this.#settingVal.width?.end}) * ${percent})`
    }
    constructor(setting: ProgressSetting = {}) {
        // 拷贝设置
        setting = deepCopy(setting)
        // 加载设置
        this.#settingVal = coverData(this.#settingVal, setting)
        // 初始化dom     
        this.el(this.#settingVal.target || document.body)
    }
    // 绑定el元素
    el(target: HTMLElement | string) {
        this.destroy()
        if (typeof target == 'string') {
            this.#target = document.querySelector(target) || document.body
        } else {
            this.#target = target
        }
        let style = getComputedStyle(this.#target)
        if (style.position == 'static') {
            this.#target.style.position = 'relative'
        }
        this.#back = document.createElement('div')
        // 初始化dom元素
        this.#bar = document.createElement('div')
        this.#back.append(this.#bar)
        this.#target.append(this.#back)
        this.#back.style.cssText = `
        position: absolute;
        top: 0px;
        width: 100%;
        overflow: hidden;
        display: none;
        height: ${this.#settingVal.height};`
        this.#bar.style.cssText = `
        width: ${this.#settingVal.width?.initial};
        height: 100%;
        transition: ${this.#settingVal.timing?.delay}ms;
        background-color: ${this.#settingVal.color?.end};
        border-radius: ${typeof this.#settingVal.width?.radius == 'string' ? this.#settingVal.width?.radius : `calc( ${this.#settingVal.width?.radius ? (this.#settingVal.height) : '0px'} / 2 )`};
        background-image: linear-gradient(to right, ${this.#settingVal.color?.start}, ${this.#settingVal.color?.end});`
    }
    // 销毁
    destroy() {
        if (!this.#back) {
            return
        }
        this.#back.remove()
        this.#back = null
    }
}